﻿using System;
using System.IO;
using System.Threading;
using SerialManager.Loaders;
using Lang = SerialManager.Localization.SerialManager;

namespace SerialManager
{
    /// <summary>
    /// Exit code used by <see cref="Core.Exit"/> method
    /// It's numeric valus is returned by Main method
    /// </summary>
    public enum ExitCode
    {
        UnknownError = -1,
        Success = 0,
        DirectoryNotFound = 1,
        DirectoryIsEmpty = 2,
        NoValidFiles = 10,
        NothingToDo = 11,
        MissingArguments = 50,
        WrongArguments = 51,
    }

    /// <summary>
    /// Main class of program
    /// </summary>
    internal class Core
    {
        private readonly static log4net.ILog Logger
            = log4net.LogManager.GetLogger( typeof( Core ) );

        /// <summary>
        /// Indicates, that application is terminating
        /// It's set by <see cref="Core.Exit"/> method
        /// </summary>
        public static bool IsTerminating { get; private set; }

        /// <summary>
        /// Title of currently processed serial (from command line)
        /// It's same name as subdirectory name relatively to working directory
        /// </summary>
        public static string SerialTitle { get; private set; }

        /// <summary>
        /// Path to currently processed serial directory
        /// </summary>
        public static DirectoryInfo SerialDirectory { get; private set; }

        /// <summary>
        /// Main entry point
        /// </summary>
        /// <param name="args"></param>
        internal static int Main( string[] args )
        {
            #region Basic initialization
            // no arguments suplied, display help and exit
            if( args.Length == 0 )
            {
                WriteHelp();
                return Exit( ExitCode.MissingArguments );
            }

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( UnhandledException );
            Thread.CurrentThread.Name = "Main";

            var param = args[0];
            if (File.Exists(param))
            {
                Logger.Info("Given parameter cannot be existing file");
                WriteHelp();
                return Exit(ExitCode.WrongArguments);
            }

            if (Directory.Exists(param))
            {
                Logger.DebugFormat("Given existing directory {0} as parameter", param);
                SerialDirectory = new DirectoryInfo(param);
                SerialTitle = SerialDirectory.Name;
            }
            else
            {
                Logger.DebugFormat("Given existing directory {0} as parameter", param);
                SerialTitle = args[0];
                SerialDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, SerialTitle));
            }
            #endregion

            #region Main loop that runs only once, for purpose of exiting program at some point
            while( true )
            {
                var builder = new FileListBuilder( SerialDirectory );
                if( builder.Status != FileListBuilderStatus.Initialized )
                    break;

                #region Reading serial information

                var loader = EpisodeLoader.Create<TvRageLoader>( SerialTitle, true );

                // wait for loader to finish or until user press abort key
                while( !loader.Finished )
                {
                    // build file list in first loop
                    if( builder.Status != FileListBuilderStatus.BuildingFinished )
                        builder.BuildFilesList();
                    // builder didn't found any valid files, information about serial is not needed
                    else if( builder.FoundFiles.Count == 0 )
                    {
                        loader.Abort(EpisodeLoaderAbortReason.NotNeeded);
                        Logger.Info( Lang.NoValidFilesFound );
                        Exit( ExitCode.NoValidFiles );
                        break;
                    }

                    // user can abort by pressing key
                    if( Console.KeyAvailable )
                    {
                        var key = Console.ReadKey( true );
                        if( key.Key == Config.AbortKey )
                        {
                            loader.Abort(EpisodeLoaderAbortReason.UserAborted);
                            break;
                        }
                    }
                }

                #endregion

                if( IsTerminating )
                    goto Exit;

                switch( loader.Status )
                {
                    case EpisodeLoaderStatus.DoneSuccessful:
                        Logger.InfoFormat( "Found information about {0} episodes in {1} seasons", loader.Episodes.Count, loader.SeasonCount );
                        break;
                    case EpisodeLoaderStatus.DoneWithError:
                        Logger.Info( "Error occured while retrieving informations." );
                        goto default;
                    case EpisodeLoaderStatus.Aborted:
                    default:
                        Logger.Info( Lang.EpisodeInformationNotUsed );
                        break;
                }

                Console.WriteLine();

                #region Moving and renaming valid files

                builder.PrepareMove( loader.Episodes );
                if( builder.Status != FileListBuilderStatus.MovementPrepared )
                    break;

                if( builder.FilesForMove.Count == 0 )
                {
                    Logger.Info( "All files has right names, nothing to be renamed." );
                    Exit( ExitCode.NothingToDo );
                    Console.ReadKey();
                }
                else
                {
                    Logger.InfoFormat( "Will rename {0} files. This operation cannot be reverted.", builder.FilesForMove.Count );
                    Console.WriteLine( "[Y]es do it, [N]o way !, [D]etail information" );

                    if( Logger.IsDebugEnabled )
                    {
                        foreach( var moveInfo in builder.FilesForMove )
                            Logger.DebugFormat( "{0} => {1}", moveInfo.Key.Name, moveInfo.Value.FullName );
                    }

                    while( true )
                    {
                        Console.TreatControlCAsInput = true;
                        var key = Console.ReadKey( true );

                        if( key.Key == ConsoleKey.D )
                        {
                            Console.WriteLine();
                            foreach( var moveInfo in builder.FilesForMove )
                            {
                                Console.WriteLine( " <= " + moveInfo.Key.FullName );
                                Console.WriteLine( " => " + moveInfo.Value );
                                Console.WriteLine();
                            }
                            Console.WriteLine( "So what now, can i do it ? Please..." );
                            Console.WriteLine( "[Y]es do it, [N]o way !" );
                            continue;
                        }

                        if( key.Key == ConsoleKey.N )
                        {
                            Console.WriteLine( "Allright, i don't touch that." );
                            break;
                        }
                        else if( key.Key == ConsoleKey.Y )
                        {
                            Console.WriteLine( "You had been warned, processing..." );
                            builder.ProcessMove();
                            Console.WriteLine( "All files has been successfully renamed." );
                            break;
                        }
                        else
                            continue;
                    }
                }
                #endregion

                break;
            }
            #endregion

        Exit:
            return Environment.ExitCode;
                
            /*
            if(  unknown.Count > 0 )
            {
                var unknownPath = Path.Combine( SerialDirectory.FullName, "!unknown" );
                Console.WriteLine();
                Console.WriteLine( "Bylo nalezeno {0} neznámých souborů. Chceš je přesunout do adresáře {1} ?", unknown.Count, unknownPath );
                Console.WriteLine( "[A]no ouhlasím, [N]echci to, [D]etailní informace" );
                Console.TreatControlCAsInput = true;
                var key = Console.ReadKey( true );
                unknown.ForEach(
                    f =>
                    {
                        if( !Directory.Exists( unknownPath ) )
                            Directory.CreateDirectory( unknownPath );
                    }
                );
            }

            if( noMatch.Count > 0 )
            {
                WriteError( "Následující soubory mají neznámý formát názvu:" );
                foreach( var noMatchItem in noMatch )
                    Console.WriteLine( noMatchItem );
            }*/
        }

        /// <summary>
        /// Handler pro zpracovani neosetrenych vyjimek
        /// </summary>
        static void UnhandledException( object sender, UnhandledExceptionEventArgs e )
        {
            if( Logger.IsDebugEnabled )
            {
                Logger.Debug( "Caught an unhandled exception." );
                Logger.Debug( e.ExceptionObject );
            }

            if( e.IsTerminating )
                Exit();
        }

        /// <summary>
        /// Ukoncit aplikaci s neznamym chybovym kodem
        /// </summary>
        public static int Exit()
        {
            return Exit( ExitCode.UnknownError );
        }

        /// <summary>
        /// Ukoncit aplikaci se zadanym kodem
        /// </summary>
        /// <param name="code"></param>
        public static int Exit( ExitCode code )
        {
            Logger.DebugFormat( "Application was terminated with code {0} ({1})", ( int )code, code );

            IsTerminating = true;
            Environment.ExitCode = ( int )code;

            Console.WriteLine();
            Console.WriteLine( "Press any key to exit" );

            Console.TreatControlCAsInput = true;
            while( !Console.KeyAvailable )
                continue;            

            return ( int )code;
        }

        /// <summary>
        /// Vypsat napovedu pokud nejsou specikovany potrebne parametery
        /// </summary>
        private static void WriteHelp()
        {
            Console.WriteLine( "Supply name of subdirectory with serial as parameter" );
        }

        /// <summary>
        /// Fixes filename for Windows system
        /// NULL is converted to empty string
        /// Removes forbidden chars
        /// </summary>
        public static string FixFileName( string filename )
        {
            if( filename == null )
                return "";
            var sb = new System.Text.StringBuilder(filename);
            var forbiddenChars = new string[]{ "*", "\"", "/", "\\", "[", "]", ":", ";", "|", "=", ",", "?"};
            foreach( var c in forbiddenChars )
                sb.Replace( c, "" );
            return sb.ToString();
        }
    }
}
